package core.observer;

import core.controllers.ControlDynamicParameters;
import core.problem.FactoryProblems;
import core.problem.Individual;
import core.problem.Population;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Administrator
 */
public class Observer {

    private List<Double> momentInertia = new LinkedList<>();//惯性矩是一个(0,1)之间的值 
    private Queue evolutionaryDataQueue = new LinkedList<>();//种群数据,可以观察种群的多样性等情况
    private List<Double> mutationProbability = new LinkedList<>();
    private List bestIndividuals = new LinkedList<>(), worstIndividuals = new LinkedList<>();
    private int generationNumber = 0;
//    private Individual historyBestIndividual;
    private double minMomentInertia = 1;
    private double maxMomentInertial = 0;
    double momentOfInertia;//惯性矩
    int t0 = 10, T = 200;//求惯性矩对应的进化代数

    public List<Double> getMomentInertia() {
        return momentInertia;
    }

    public int getGenerationNumber() {
        return generationNumber;
    }

    public void setGenerationNumber(int generationNumber) {
        this.generationNumber = generationNumber;
    }

    public void saveStatus(int generation, Population population) {
        momentOfInertia = getMomentOfInertia(population);
        minMomentInertia = getMinMomentInertia() < momentOfInertia ? getMinMomentInertia() : momentOfInertia;
        maxMomentInertial = getMaxMomentInertial() > momentOfInertia ? getMaxMomentInertial() : momentOfInertia;
        this.momentInertia.add(momentOfInertia);

        this.generationNumber = generation;

        save2History(population);

        System.out.println(generation + "\t" + ((Individual) bestIndividuals.get(bestIndividuals.size() - 1)).getDecisionVariable().getX() + "\t"
                + ((Individual) bestIndividuals.get(bestIndividuals.size() - 1)).getDecisionVariable().getFitnessString());
    }

    private void save2History(Population population) {
        List<Individual> individualList = new LinkedList<>();
        Individual bestIndividual = (Individual) population.getIndividuals().get(0),
                worstIndividual = (Individual) population.getIndividuals().get(0);
        try {
            for (int i = 0; i < population.getIndividuals().size(); i++) {
                individualList.add(((Individual) population.getIndividuals().get(i)).clone());//
                if (((Individual) population.getIndividuals().get(i)).compareTo(bestIndividual) >= 0) {
                    bestIndividual = (Individual) population.getIndividuals().get(i);
                } else {//不比最优的好，所以要和差的比
                    if (((Individual) population.getIndividuals().get(i)).compareTo(worstIndividual) < 0) {
                        worstIndividual = (Individual) population.getIndividuals().get(i);
                    }
                }
            }
            bestIndividuals.add(bestIndividual.clone());
            worstIndividuals.add(worstIndividual.clone());
            population.setBestIndividual(bestIndividual.clone());
            population.setWorstIndividual(worstIndividual.clone());
        } catch (CloneNotSupportedException ex) {
            Logger.getLogger(Observer.class.getName()).log(Level.SEVERE, null, ex);
        }
        ControlDynamicParameters.currentAlgorithm.getObserver().getEvolutionaryDataQueue().add(individualList);

    }

    public double getMinMomentInertia() {
        return minMomentInertia;
    }

    public double getMaxMomentInertial() {
        return maxMomentInertial;
    }

    public Queue getEvolutionaryDataQueue() {
        return evolutionaryDataQueue;
    }

    public void initQueue() {
        evolutionaryDataQueue = new LinkedList();
    }

    public List<Double> getMutationProbability() {
        return mutationProbability;
    }

    public List getBestIndividuals() {
        return bestIndividuals;
    }

    public double getMomentOfInertia(Population population) {
        Observer observer = ControlDynamicParameters.currentAlgorithm.getObserver();
        double value, sum;
        momentOfInertia = 0;
        double momentInertiaLower, momentInertiaUpper;
        double[][] randScope = FactoryProblems.currentProblem.getVariableProperties();
        List<Individual> individuals=population.getIndividuals();
        double[] centroids = new double[((Individual)population.getIndividuals().get(0)).getDecisionVariable().getGeneCodes().length];
        switch (FactoryProblems.currentProblem.getDecisionVariable().getGenecodeType()) {
            case DOUBLECODE:
                for (int i = 0; i < centroids.length; i++) {
                    sum = 0;
                    for (int j = 0; j < individuals.size(); j++) {
                        sum += individuals.get(j).getDecisionVariable().getGeneCodes()[i];
                    }
                    centroids[i] = sum / individuals.size();
                }
                for (int i = 0; i < individuals.size(); i++) {
                    for (int j = 0; j < centroids.length; j++) {
                        value = (individuals.get(i).getDecisionVariable().getGeneCodes()[j] - centroids[j]) / (randScope[j][1] - randScope[j][0]);
                        momentOfInertia += Math.pow(value, 2);
                    }
                }
                momentOfInertia = momentOfInertia / (individuals.size() * centroids.length);
                break;
            case BINARYCODE:
                for (int i = 0; i < individuals.size() - 1; i++) {
                    for (int j = i + 1; j < individuals.size(); j++) {
                        for (int k = 0; k < FactoryProblems.currentProblem.getDecisionVariable().getGeneralGenecodeString().length(); k++) {
                            if (individuals.get(i).getDecisionVariable().getGeneralGenecodeString().charAt(k) == individuals.get(j).getDecisionVariable().getGeneralGenecodeString().charAt(k)) {
                                momentOfInertia += 0;
                            } else {
                                momentOfInertia += 1;
                            }
                        }
                    }
                }
                momentOfInertia = momentOfInertia / ((individuals.size() - 1) * individuals.size() * FactoryProblems.currentProblem.getDecisionVariable().getGeneralGenecodeString().length());
                break;
            default:
                System.out.println("还不支持");
                break;
        }
        if (observer.getGenerationNumber() > t0) {
            momentInertiaLower = observer.getMinMomentInertia() + 0.25 * Math.exp(-(observer.getGenerationNumber() / T)) * (observer.getMaxMomentInertial() - observer.getMinMomentInertia());
            momentInertiaUpper = observer.getMinMomentInertia() + 0.75 * Math.exp(-(observer.getGenerationNumber() / T)) * (observer.getMaxMomentInertial() - observer.getMinMomentInertia());
            if (momentOfInertia < momentInertiaLower) {
                ControlDynamicParameters.mutationProbability = (2 - momentOfInertia / momentInertiaLower) * ControlDynamicParameters.mutationProbability;
            } else {
                if (momentOfInertia > momentInertiaUpper) {
                    ControlDynamicParameters.mutationProbability = momentInertiaUpper / momentOfInertia * ControlDynamicParameters.mutationProbability;
                } else {
                    ControlDynamicParameters.mutationProbability = 0.01;
                }
            }
        }
        ControlDynamicParameters.currentAlgorithm.getObserver().getMutationProbability().add(ControlDynamicParameters.mutationProbability);
        return momentOfInertia;
    }
}
